#include "ScreenCaptureEngine.h"
#include <QStyleOption>
#include <QPainter>
#include <QScreen>
#include <QApplication>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QDesktopWidget>
#ifdef QT_DEBUG
#include <QDebug>
#include <QDateTime>
#endif
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <algorithm>
#include <iterator>

using namespace cv::xfeatures2d;

const int CaptureView::nCaptureBorder = 1;
const QColor CaptureView::borderColor = QColor(93, 190, 138);
CaptureView* CaptureView::gCaptureView = nullptr;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);

void calcCorners(std::vector<Point2f>& Conners, const Mat& homo, const Mat& input)
{
    if(Conners.size() < 4)
        Conners.resize(4);
    double v2[]={0,0,1};
    double v1[3];//改变后值
    Mat V2=Mat(3,1,CV_64FC1,v2);
    Mat V1=Mat(3,1,CV_64FC1,v1);

    // topLeft
    V1 = homo * V2;
    Conners[0].x=v1[0]/v1[2];
    Conners[0].y=v1[1]/v1[2];

    // bottomLeft
    v2[0]=0;
    v2[1]=input.rows;
    v2[2]=1;
    V2=Mat(3,1,CV_64FC1,v2);
    V1=Mat(3,1,CV_64FC1,v1);
    V1=homo*V2;
    Conners[1].x=v1[0]/v1[2];
    Conners[1].y=v1[1]/v1[2];

    // topRight
    v2[0]=input.cols;
    v2[1]=0;
    v2[2]=1;
    V2=Mat(3,1,CV_64FC1,v2);
    V1=Mat(3,1,CV_64FC1,v1);
    V1=homo*V2;
    Conners[2].x=v1[0]/v1[2];
    Conners[2].y=v1[1]/v1[2];

    // bottomRight
    v2[0]=input.cols;
    v2[1]=input.rows;
    v2[2]=1;
    V2=Mat(3,1,CV_64FC1,v2);
    V1=Mat(3,1,CV_64FC1,v1);
    V1=homo*V2;
    Conners[3].x=v1[0]/v1[2];
    Conners[3].y=v1[1]/v1[2];
}

// Mat转QImage
QImage cvMatToQImage( const cv::Mat &inMat )
{
    switch ( inMat.type() )
    {
    // 8-bit, 4 channel
    case CV_8UC4:
    {
        QImage image( inMat.data,
                      inMat.cols, inMat.rows,
                      static_cast<int>(inMat.step),
                      QImage::Format_ARGB32 );

        return image;
    }

        // 8-bit, 3 channel
    case CV_8UC3:
    {
        QImage image( inMat.data,
                      inMat.cols, inMat.rows,
                      static_cast<int>(inMat.step),
                      QImage::Format_RGB888 );

        return image.rgbSwapped();
    }

        // 8-bit, 1 channel
    case CV_8UC1:
    {
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
        QImage image( inMat.data,
                      inMat.cols, inMat.rows,
                      static_cast<int>(inMat.step),
                      QImage::Format_Grayscale8 );//Format_Alpha8 and Format_Grayscale8 were added in Qt 5.5
#else//这里还有一种写法，最后给出
        static QVector<QRgb>  sColorTable;

        // only create our color table the first time
        if ( sColorTable.isEmpty() )
        {
            sColorTable.resize( 256 );

            for ( int i = 0; i < 256; ++i )
            {
                sColorTable[i] = qRgb( i, i, i );
            }
        }

        QImage image( inMat.data,
                      inMat.cols, inMat.rows,
                      static_cast<int>(inMat.step),
                      QImage::Format_Indexed8 );

        image.setColorTable( sColorTable );
#endif

        return image;
    }
    default:
        break;
    }

    return QImage();
}

// Mat转QPixmap
QPixmap cvMatToQPixmap( const cv::Mat &inMat )
{
    return QPixmap::fromImage( cvMatToQImage( inMat ) );
}

// QImage转Mat
Mat QImageToCvMat( const QImage &inImage, bool inCloneImageData = true )
{
    switch ( inImage.format() )
    {
    // 8-bit, 4 channel
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
    {
        cv::Mat  mat( inImage.height(), inImage.width(),
                      CV_8UC4,
                      const_cast<uchar*>(inImage.bits()),
                      static_cast<size_t>(inImage.bytesPerLine())
                      );

        return (inCloneImageData ? mat.clone() : mat);
    }

        // 8-bit, 3 channel
    case QImage::Format_RGB32:
    case QImage::Format_RGB888:
    {
        if ( !inCloneImageData )
        {
        }

        QImage   swapped = inImage;

        if ( inImage.format() == QImage::Format_RGB32 )
        {
            swapped = swapped.convertToFormat( QImage::Format_RGB888 );
        }

        swapped = swapped.rgbSwapped();

        return cv::Mat( swapped.height(), swapped.width(),
                        CV_8UC3,
                        const_cast<uchar*>(swapped.bits()),
                        static_cast<size_t>(swapped.bytesPerLine())
                        ).clone();
    }

        // 8-bit, 1 channel
    case QImage::Format_Indexed8:
    {
        cv::Mat  mat( inImage.height(), inImage.width(),
                      CV_8UC1,
                      const_cast<uchar*>(inImage.bits()),
                      static_cast<size_t>(inImage.bytesPerLine())
                      );

        return (inCloneImageData ? mat.clone() : mat);
    }

    default:
        break;
    }

    return cv::Mat();
}

// QPixmap转Mat
Mat QPixmapToCvMat( const QPixmap &inPixmap, bool inCloneImageData = true )
{
    return QImageToCvMat( inPixmap.toImage(), inCloneImageData );
}


CaptureView::CaptureView(QWidget *parent) :
    QWidget(parent),
    m_bLPressed(false),
    m_bLFinished(false),
    m_bScrollCapture(false)
{
#ifdef Q_OS_WINDOWS
    m_mouseHookHandler = NULL;
#endif
    setObjectName(QStringLiteral("capture_bg_view"));
    setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TransparentForMouseEvents, false);
    setAttribute(Qt::WA_TranslucentBackground, true);
    setWindowState(Qt::WindowActive | Qt::WindowFullScreen);
    m_pScreen = QApplication::primaryScreen();
    m_screenRect = m_pScreen->geometry();
    setFixedSize(m_screenRect.width(), m_screenRect.height());
    setStyleSheet(QStringLiteral("QWidget#capture_bg_view{border:none;}"));

    // 初始化其它控件
    m_screenImage = m_pScreen->grabWindow(QApplication::desktop()->winId(),
                                          0,
                                          0,
                                          m_pScreen->geometry().width(),
                                          m_pScreen->geometry().height());
    m_pLabelSize = new QLabel(this);
    m_pLabelSize->setStyleSheet(QStringLiteral("QLabel{color:white;font-family:Microsoft Yahei;font-size:15px;background-color:black;}"));

    m_pLabelPrompt = new QLabel(this);
    m_pLabelPrompt->setStyleSheet(QStringLiteral("QLabel{color:white;font-family:Microsoft Yahei;font-size:20px;background-color:black;}"));
    m_pLabelPrompt->hide();

    //
    m_pToolBar = new ToolBar(this);
    QPushButton* pBtnComplete = new QPushButton(QStringLiteral("完成"), m_pToolBar); // 截图完成
    QPushButton* pBtnScrollCapture = new QPushButton(QStringLiteral("长截图"), m_pToolBar);
    pBtnComplete->setObjectName(QStringLiteral("btn_complete"));
    pBtnComplete->setCursor(QCursor(Qt::PointingHandCursor));
    pBtnComplete->setStyleSheet(QStringLiteral("QPushButton#btn_complete{color:white;font-family:Microsoft Yahei;border:none;font-size:18px;background-color:black;}"));
    pBtnComplete->setFixedSize(60, 22);

    connect(pBtnComplete, &QPushButton::clicked, this, [&]{
        if(m_bScrollCapture)
        {
            FinishCallback(true, m_scrollImage);
        }
        else
        {
            FinishCallback(true, m_captureImage);
        }
    });
    connect(this, &CaptureView::EnableScrollTimer, this, [&]{
        if(m_scrollTimer.isActive())
            m_scrollTimer.stop();
        m_scrollTimer.start(nScrollIntervalBase);
    });
    connect(&m_scrollTimer, &QTimer::timeout, this, [&]{
        m_scrollTimer.stop();
    });
    connect(this, &CaptureView::ShowPrompt, this, [&](const QString& str){
        m_pLabelPrompt->setText(str);
        m_pLabelPrompt->adjustSize();
        m_pLabelPrompt->move(m_captureRect.x() + (m_captureRect.width() - m_pLabelPrompt->width()) / 2,
                             m_captureRect.y() + (m_captureRect.height() - m_pLabelPrompt->height()) / 2 );
        if(m_pLabelPrompt->isHidden())
            m_pLabelPrompt->show();
        if(!m_scrollPromptTimer.isActive())
            m_scrollPromptTimer.start(m_nPromptDuration);
    });
    connect(&m_scrollPromptTimer, &QTimer::timeout, this, [&]{
        if(!m_pLabelPrompt->isHidden())
            m_pLabelPrompt->hide();
    });
    pBtnScrollCapture->setCursor(QCursor(Qt::PointingHandCursor));
    pBtnScrollCapture->setStyleSheet(QStringLiteral("QPushButton{color:white;font-family:Microsoft Yahei;border:none;font-size:18px;background-color:black;}"));
    pBtnScrollCapture->setFixedSize(80, 22);
    connect(pBtnScrollCapture, &QPushButton::clicked, this, [=]{
#ifdef Q_OS_WINDOWS
        if(!InstallHook())
            return;
        if(!m_bScrollCapture)
        {
            m_bScrollCapture = true;
            // TODO: 按下长截图后，先截当前区域一次
            m_scrollImage = m_pScreen->grabWindow(QApplication::desktop()->winId(),
                                                  m_captureRect.x() + nCaptureBorder,
                                                  m_captureRect.y() + nCaptureBorder,
                                                  m_captureRect.width() - nCaptureBorder,
                                                  m_captureRect.height() - nCaptureBorder);
            m_stLastData.lastImage = m_scrollImage;
            m_stLastData.y = 0; // 第一次从0开始拼接
            PlaceScrollPreview();
        }
        pBtnScrollCapture->hide();
        update();
#endif
    });
    m_pToolBar->Push(pBtnScrollCapture);
    m_pToolBar->Push(pBtnComplete);
    //

    connect(this, &CaptureView::Scrolled, this, &CaptureView::ProcessScrollEvent, Qt::BlockingQueuedConnection);

    //
    m_pToolBar->hide();
    m_pLabelSize->hide();
    setMouseTracking(true);
    gCaptureView = this;

    installEventFilter(this);
}

CaptureView::~CaptureView()
{
#ifdef Q_OS_WINDOWS
#ifdef QT_DEBUG
    qDebug() << "~CaptureView";
#endif
#endif
    UninstallHook();
}

bool CaptureView::IsInCaptureArea(int x, int y) const
{
    return m_captureRect.contains(x, y);
}

void CaptureView::Capture(const QRect& rect)
{
    if(rect == QRect())
    {
        m_captureImage = m_pScreen->grabWindow(QApplication::desktop()->winId(),
                                               m_captureRect.x() + nCaptureBorder,
                                               m_captureRect.y() + nCaptureBorder,
                                               m_captureRect.width() - nCaptureBorder,
                                               m_captureRect.height() - nCaptureBorder);
    }
    else
    {
        m_captureImage = m_pScreen->grabWindow(QApplication::desktop()->winId(),
                                               rect.x(),
                                               rect.y(),
                                               rect.width(),
                                               rect.height());
    }
}

void CaptureView::ScrollCapture(short nDelta)
{
    if(m_scrollImage.height() >= nScrollMaxHeight)
    {
        emit ShowPrompt(QStringLiteral("已达到截图最大长度！"));
        return;
    }
    m_seQueue.Push([&, nDelta]{
        std::this_thread::sleep_for(std::chrono::milliseconds(nScrollIntervalBase << 1));
        CaptureInformation ci;
        ci.nDelta = nDelta;
        if(m_enOrientation != Embedded_Bottom && m_enOrientation != Embedded_Top)
        {
            Capture();
        }
        else
        {
            int h = m_captureRect.height() - m_scrollImageBk.height() - nScrollPreviewMargin;;
            if(m_enOrientation == Embedded_Top)
            {

                Capture(QRect(m_captureRect.x() + nCaptureBorder,
                              m_captureRect.y() + nCaptureBorder + m_captureRect.height() - h - 1,
                              m_captureRect.width() - nCaptureBorder,
                              h));
            }
            else
            {
                h = m_captureRect.height() - m_scrollImageBk.height() - nScrollPreviewMargin;
                Capture(QRect(m_captureRect.x() + nCaptureBorder,
                              m_captureRect.y() + nCaptureBorder,
                              m_captureRect.width() - nCaptureBorder,
                              h));
            }
        }

        ci.pixmap = m_captureImage;
        emit Scrolled(ci);
    });
}

void CaptureView::StartScrollTimer()
{
    emit EnableScrollTimer();
}

bool CaptureView::IsScrollTimerActive() const
{
    return m_scrollTimer.isActive();
}

CaptureView::CaptureOrientation CaptureView::PreviewOrientation() const
{
    return m_enOrientation;
}

void CaptureView::ChangePreviewOrientation(CaptureOrientation ori)
{
    m_enOrientation = ori;
    PlaceScrollPreview();
    update();
}

bool CaptureView::IsContinueScroll() const
{
    return !(m_scrollImage.height() >= nScrollMaxHeight);
}

bool CaptureView::MatchTemplate(const Mat& src_gray, const Mat &templateImage)
{
    Mat temp_gray;
    cvtColor(templateImage, temp_gray, COLOR_BGR2GRAY);
    Mat matResult;
    int result_cols = src_gray.cols - temp_gray.cols + 1;
    int result_rows = src_gray.rows - temp_gray.rows + 1;
#ifdef QT_DEBUG
    qDebug() << "temp_gray width is " << temp_gray.cols << " and height is " << temp_gray.rows;
    qDebug() << "result_cols is " << result_cols << " and result_rows is " << result_rows;
#endif
    matResult.create(result_cols, result_rows, CV_32FC1);
    matchTemplate(src_gray, temp_gray, matResult, TM_CCORR_NORMED);
    normalize(matResult, matResult, 0, 1, NORM_MINMAX, -1);
    cv::Point minPoint;
    cv::Point maxPoint;
    double minVal;
    double maxVal;
    cv::minMaxLoc(matResult, &minVal, &maxVal, &minPoint, &maxPoint);
    if((maxVal - 1.0000f) >= 0.0000f)
    {
#ifdef QT_DEBUG
        qDebug() << "match result is true";
#endif
        return true;
    }
    else
    {
#ifdef QT_DEBUG
        qDebug() << "match result is false";
#endif
        return false;
    }
}

void CaptureView::ProcessScrollEvent(CaptureInformation info)
{
    if(m_stLastData.delta * info.nDelta < 0)
    {
        m_stLastData.delta = info.nDelta;
        m_stLastData.bInReverse = true;
        if(info.nDelta > 0)
            m_stLastData.y = 0;
        else
            m_stLastData.y = m_scrollImage.height() - 1;
    }

    m_stLastData.delta = info.nDelta;

    short nDelta = info.nDelta;
    Mat src;
    if(!m_stLastData.bInReverse)
        src = QPixmapToCvMat(m_stLastData.lastImage);
    else
        src = QPixmapToCvMat(m_scrollImage);
    Mat templateImage = QPixmapToCvMat(info.pixmap);

    Mat src_gray, temp_gray;
    cvtColor(src, src_gray, COLOR_RGB2GRAY);
    cvtColor(templateImage, temp_gray, COLOR_RGB2GRAY);

    // 1. 初始化
    std::vector<KeyPoint> keypoints1, keypoints2;
    Mat descriptors1, descriptors2;

    Ptr<ORB> orb = ORB::create();

    // 2. 提取特征点
    orb->detect(src_gray, keypoints1);
    orb->detect(temp_gray, keypoints2);

    // 3. 计算特征描述符
    orb->compute(src_gray, keypoints1, descriptors1);
    orb->compute(temp_gray, keypoints2, descriptors2);

    if(descriptors1.rows == 0 || descriptors2.rows == 0 ||
            descriptors1.cols == 0 || descriptors2.cols == 0)
        return;

    // 4. 对两幅图像的BRIEF描述符进行匹配，使用BFMatch，Hamming距离作为参考
    const float minRatio = 1.f / 1.5f;
    const int k = 2;
    std::vector<DMatch> matches;
    std::vector<std::vector<DMatch>> knnMatches;
    BFMatcher bfMatcher(NORM_HAMMING);
    bfMatcher.knnMatch(descriptors1, descriptors2, knnMatches, k);

    for (size_t i = 0; i < knnMatches.size(); i++)
    {
        const DMatch& bestMatch = knnMatches[i][0];
        const DMatch& betterMatch = knnMatches[i][1];
        float distanceRatio = bestMatch.distance / betterMatch.distance;
        if (distanceRatio < minRatio)
            matches.push_back(bestMatch);
    }

#ifdef QT_DEBUG
    qDebug() << "matches_size is " << matches.size();
#endif

    if(keypoints2.size() - matches.size() < 20)
        return;

    std::vector<Point2f> imagePoints0, imagePoints1;
    for (size_t i = 0; i < matches.size(); i++)
    {
        imagePoints0.push_back(keypoints1[matches[i].queryIdx].pt);
        imagePoints1.push_back(keypoints2[matches[i].trainIdx].pt);
    }

    if(imagePoints0.size() < 4 || imagePoints1.size() < 4)
        return;

    Mat homo = findHomography(imagePoints0, imagePoints1, RANSAC);
    if(homo.rows <= 0 || homo.cols <= 0)
    {
#ifdef QT_DEBUG
        qDebug() << "homo_error";
#endif
        return;
    }
    std::vector<Point2f> corners;
    calcCorners(corners, homo, src_gray);

    Mat imageTransform1;
    warpPerspective(src_gray, imageTransform1, homo, Size(MAX(corners[1].x, corners[3].x), temp_gray.rows));

    int tp1 = 0;
    Mat temp1;
    if(nDelta > 0)
    {
        tp1 = corners[0].y > corners[2].y ? corners[0].y : corners[2].y;
        if(tp1 < 0 || tp1 > templateImage.rows)
        {
#ifdef QT_DEBUG
            qDebug() << "tp1_error";
#endif
            return;
        }
        temp1 = templateImage(Rect(0, 0, templateImage.cols, tp1));
    }
    else
    {
        tp1 = corners[1].y > corners[3].y ? corners[1].y : corners[3].y;
        if(tp1 < 0 || templateImage.rows - tp1 <= 0)
        {
#ifdef QT_DEBUG
            qDebug() << "tp1_error";
#endif
            return;
        }
        temp1 = templateImage(Rect(0, tp1, templateImage.cols, templateImage.rows - tp1));
    }
    if(temp1.rows <= 0 || temp1.cols <= 0)
        return;
    if(m_stLastData.bInReverse)
    {
        m_stLastData.bInReverse = false;
    }

    Mat dst = QPixmapToCvMat(m_scrollImage);
    if(!m_stLastData.bInReverse)
    {
        if(m_stLastData.y == 0)
        {
            Mat dst_2(dst.rows + temp1.rows, dst.cols, dst.type());
            dst_2.setTo(0);
            if(nDelta > 0)
            {
                dst_2 = Mat(dst.rows + temp1.rows, dst.cols, dst.type());
                dst_2.setTo(0);
                temp1.copyTo(dst_2(Rect(0, 0, temp1.cols, temp1.rows)));
                dst.copyTo(dst_2(Rect(0, temp1.rows, dst.cols, dst.rows)));
                m_stLastData.y = 0;
            }
            else
            {
                dst.copyTo(dst_2(Rect(0, 0, dst.cols, dst.rows)));
                temp1.copyTo(dst_2(Rect(0, dst.rows, temp1.cols, temp1.rows)));
                m_stLastData.y += dst_2.rows;
            }
            m_scrollImage = cvMatToQPixmap(dst_2);
        }
        else
        {
            Mat dst_2;
            int y = m_stLastData.y;
            if(nDelta > 0)
            {
                return;
            }
            else
            {
                Mat dst_cut = dst(Rect(0, 0, dst.cols, y));
                dst_2 = Mat(y + temp1.rows, dst.cols, dst.type());
                dst_2.setTo(0);
                dst_cut.copyTo(dst_2(Rect(0, 0, dst_cut.cols, dst_cut.rows)));
                temp1.copyTo(dst_2(Rect(0, y, temp1.cols, temp1.rows)));
                m_stLastData.y += temp1.rows;
            }
            m_scrollImage = cvMatToQPixmap(dst_2);
        }
    }

    PlaceScrollPreview();
    m_stLastData.lastImage = info.pixmap;

    update();
}

bool CaptureView::InstallHook()
{
#ifdef Q_OS_WINDOWS
    if(m_mouseHookHandler != NULL)
        return true;
    m_mouseHookHandler = ::SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)MouseProc, NULL, 0);
    if(m_mouseHookHandler == NULL)
    {
#ifdef QT_DEBUG
        qDebug() << "install_hook_failed";
#endif
        return false;
    }
#ifdef QT_DEBUG
    qDebug() << "install_hook_success";
#endif
    return true;
#endif
}

#ifdef Q_OS_WINDOWS
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if(wParam == WM_MOUSEWHEEL)
    {
        if(CaptureView::gCaptureView->IsScrollTimerActive())
        {
            return TRUE;
        }
        if(!CaptureView::gCaptureView->IsContinueScroll())
        {
            // 如果到达最大高度，也进入ScrollCapture，但会直接弹出提示
            CaptureView::gCaptureView->ScrollCapture(0);
            return TRUE;
        }
        MSLLHOOKSTRUCT* mWheelDStruct = (MSLLHOOKSTRUCT*)lParam;
        short mouseData = (short)((mWheelDStruct->mouseData >> 16) & 0xFFFF);
        if(mouseData > 0 && CaptureView::gCaptureView->PreviewOrientation() == CaptureView::CaptureOrientation::Embedded_Top)
        {
            // 如果向上滚动且内在右上角，这时候要把内嵌的位置改为右下角
            CaptureView::gCaptureView->ChangePreviewOrientation(CaptureView::CaptureOrientation::Embedded_Bottom);
        }
        else
        {
            if(mouseData < 0 && CaptureView::gCaptureView->PreviewOrientation() == CaptureView::CaptureOrientation::Embedded_Bottom)
            {
                CaptureView::gCaptureView->ChangePreviewOrientation(CaptureView::CaptureOrientation::Embedded_Top);
            }
        }
        CaptureView::gCaptureView->ScrollCapture(mouseData);
        CaptureView::gCaptureView->StartScrollTimer();
    }
    else if (wParam == WM_LBUTTONDOWN || wParam == WM_LBUTTONUP)
    {
        // TODO: 鼠标按下后，要判断按下的地方是哪里
        PMOUSEHOOKSTRUCT p = (PMOUSEHOOKSTRUCT)lParam;
        bool ret = CaptureView::gCaptureView->IsInCaptureArea(p->pt.x, p->pt.y);
        if(ret)
        {
            return TRUE;
        }
    }
    else
    {
        if(wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK ||
                wParam == WM_RBUTTONDOWN || wParam == WM_NCRBUTTONUP ||
                wParam == WM_MOUSEHOVER || wParam == WM_NCMOUSEHOVER)
            return TRUE;
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}
#endif

void CaptureView::UninstallHook()
{
#ifdef Q_OS_WINDOWS
    if(m_mouseHookHandler != NULL)
    {
        UnhookWindowsHookEx(m_mouseHookHandler);
#ifdef QT_DEBUG
        qDebug() << "uninstall_hook_success";
#endif
    }
#endif
}

bool CaptureView::eventFilter(QObject *watched, QEvent *event)
{
    if(event->type() == QEvent::KeyPress)
    {
#ifndef QT_DEBUG
        FinishCallback(false, QPixmap());
        //deleteLater();
        return true;
#endif
    }
    return QWidget::eventFilter(watched, event);
}

void CaptureView::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    // 滚动时候，绘制m_screenImage会显得很奇怪，不画
    if(!m_bScrollCapture)
        p.drawPixmap(0, 0, m_screenImage);
    p.fillRect(rect(), QColor(128, 128, 128, 128));

    QPen pen(borderColor);
    pen.setWidth(nCaptureBorder);
    p.setPen(pen);

    //
    QPointF diff = m_ptCurrent - m_ptPressed;
    if(m_bLPressed)
    {
        if(!m_bLFinished)
        {
            if(diff.x() != 0 || diff.y() != 0)
            {
                m_captureRect = QRect(m_ptPressed, m_ptCurrent);

                m_pLabelSize->setText(QString::number(std::abs(diff.x())) +
                                      QStringLiteral(" x ") +
                                      QString::number(std::abs(diff.y())));
                m_pLabelSize->adjustSize();
                PlaceLabelSize(diff);
                if(m_pLabelSize->isHidden())
                    m_pLabelSize->show();

                CaptureRegionTransform(diff);
                p.drawRect(m_captureRect);
                m_captureImage = m_screenImage.copy(QRect(m_captureRect.x() + nCaptureBorder,
                                                          m_captureRect.y() + nCaptureBorder,
                                                          m_captureRect.width() - nCaptureBorder,
                                                          m_captureRect.height() - nCaptureBorder));
                p.drawPixmap(QRect(m_captureRect.x() + nCaptureBorder,
                                   m_captureRect.y() + nCaptureBorder,
                                   m_captureRect.width() - nCaptureBorder,
                                   m_captureRect.height() - nCaptureBorder), m_captureImage);
            }
        }
        else
        {
            PlaceLabelSize(diff);
            p.drawRect(m_captureRect);
            m_captureImage = m_screenImage.copy(QRect(m_captureRect.x() + nCaptureBorder,
                                                      m_captureRect.y() + nCaptureBorder,
                                                      m_captureRect.width() - nCaptureBorder,
                                                      m_captureRect.height() - nCaptureBorder));
            p.drawPixmap(QRect(m_captureRect.x() + nCaptureBorder,
                               m_captureRect.y() + nCaptureBorder,
                               m_captureRect.width() - nCaptureBorder,
                               m_captureRect.height() - nCaptureBorder), m_captureImage);
        }
    }
    else
    {
        if(m_bLFinished)
        {
            if(m_pLabelSize->isHidden())
                m_pLabelSize->show();
            p.drawRect(m_captureRect);
            if(!m_bScrollCapture)
            {
                p.drawPixmap(QRect(m_captureRect.x() + nCaptureBorder,
                                   m_captureRect.y() + nCaptureBorder,
                                   m_captureRect.width() - nCaptureBorder,
                                   m_captureRect.height() - nCaptureBorder), m_captureImage);
            }
            else
            {
                p.save();
                m_pLabelSize->setText(QString::number(m_scrollImage.width()) +
                                      QStringLiteral(" x ") +
                                      QString::number(m_scrollImage.height()));
                m_pLabelSize->adjustSize();
                p.setCompositionMode(QPainter::CompositionMode_Clear);
                p.fillRect(QRect(m_captureRect.x() + nCaptureBorder,
                                 m_captureRect.y() + nCaptureBorder,
                                 m_captureRect.width() - nCaptureBorder,
                                 m_captureRect.height() - nCaptureBorder), Qt::SolidPattern);
                p.restore();
                p.drawRect(m_nScrollPreviewX - nCaptureBorder,
                           m_nScrollPreviewY - nCaptureBorder,
                           m_scrollImageBk.width() + nCaptureBorder * 2,
                           m_scrollImageBk.height() + nCaptureBorder * 2);
                p.drawPixmap(m_nScrollPreviewX, m_nScrollPreviewY, m_scrollImageBk);
            }
        }
    }
    //
    QWidget::paintEvent(event);
}

void CaptureView::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        if(!m_bLPressed)
        {
            m_bLPressed = true;
        }
        if(m_bLFinished)
        {
            if(cursor() == Qt::SizeAllCursor)
                m_ptPressed = event->pos() - m_captureRect.topLeft();
        }
        else
            m_ptPressed = event->pos();
    }
    else
    {
        QWidget::mousePressEvent(event);
    }
}

void CaptureView::mouseMoveEvent(QMouseEvent *event)
{
    if(m_bLPressed)
    {
        m_ptCurrent = event->pos();
        if(m_bLFinished)
        {
            if(cursor() == Qt::SizeAllCursor)
            {
                m_captureRect.moveTo(event->globalPos() - m_ptPressed);
                PlaceToolBar();
            }
        }
        update();
    }
    else
    {
        if(m_bLFinished)
        {
            if(m_captureRect.contains(event->pos()))
            {
                if(cursor() != Qt::SizeAllCursor)
                    setCursor(QCursor(Qt::SizeAllCursor));
            }
            else
            {
                if(cursor() != Qt::ArrowCursor)
                    setCursor(QCursor(Qt::ArrowCursor));
            }
        }
    }
    QWidget::mouseMoveEvent(event);
}

void CaptureView::mouseReleaseEvent(QMouseEvent *event)
{
    if(m_bLPressed)
    {
        if(!m_bLFinished)
        {
            m_bLFinished = true;
            if(m_pToolBar->isHidden())
                m_pToolBar->show();
            PlaceToolBar();
        }
        m_bLPressed = false;
        update();
    }
    QWidget::mouseReleaseEvent(event);
}

void CaptureView::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Escape)
    {
        FinishCallback(false, QPixmap());
        //deleteLater();
    }
    else
    {
        QWidget::keyPressEvent(event);
    }
}

void CaptureView::PlaceLabelSize(const QPointF& ptDiff)
{
    int height = m_pLabelSize->height();
    if(m_bLFinished)
    {
        if(m_captureRect.top() >= height)
        {
            m_pLabelSize->move(m_captureRect.left(),
                               m_captureRect.top() - height);
        }
        else
        {
            m_pLabelSize->move(m_captureRect.left() + nCaptureBorder,
                               m_captureRect.top() + nCaptureBorder);
        }
    }
    else
    {
        if(ptDiff.x() > 0 && ptDiff.y() > 0)
        {
            // TODO: x大于0，y大于0，则往右下角移动
            // 此时label要显示在m_ptPressed
            if(m_ptPressed.y() < height)
            {
                m_pLabelSize->move(m_ptPressed.x(), m_ptPressed.y());
            }
            else
            {
                m_pLabelSize->move(m_ptPressed.x(), m_ptPressed.y() - height);
            }
        }
        else if(ptDiff.x() > 0 && ptDiff.y() < 0)
        {
            // TODO: x大于0，y小于0，则往右上角移动
            // 此时label要显示在m_ptPressed + (0, ptDiff.y())
            if(m_ptPressed.y() + ptDiff.y() < height)
            {
                m_pLabelSize->move(m_ptPressed.x() + nCaptureBorder, m_ptPressed.y() + ptDiff.y() + nCaptureBorder);
            }
            else
            {
                m_pLabelSize->move(m_ptPressed.x(), m_ptPressed.y() + ptDiff.y() - height);
            }
        }
        else if(ptDiff.x() < 0 && ptDiff.y() > 0)
        {
            // TODO: x小于0，y大于0，则往左下角移动
            // 此时label要显示在m_ptPressed + (ptDiff.x(), 0)
            if(m_ptPressed.y() < height)
            {
                m_pLabelSize->move(m_ptPressed.x() + ptDiff.x(), m_ptPressed.y());
            }
            else
            {
                m_pLabelSize->move(m_ptPressed.x() + ptDiff.x(), m_ptPressed.y() - height);
            }
        }
        else if(ptDiff.x() < 0 && ptDiff.y() < 0)
        {
            // TODO: x小于0，y小于0，则往左上角移动
            // 此时label要显示在m_ptPressed + (ptDiff.x(), ptDiff.y())
            if(m_ptPressed.y() + ptDiff.y() < height)
            {
                m_pLabelSize->move(m_ptPressed.x() + ptDiff.x() + nCaptureBorder, m_ptPressed.y() + ptDiff.y() + nCaptureBorder);
            }
            else
            {
                m_pLabelSize->move(m_ptPressed.x() + ptDiff.x(), m_ptPressed.y() + ptDiff.y() - height);
            }
        }
        else
        {
            // TODO: 暂时不做处理
        }
    }
}

void CaptureView::PlaceToolBar()
{
    if(height() - m_captureRect.top() - m_captureRect.height() < m_pToolBar->height())
    {
        m_pToolBar->move(m_captureRect.x() + m_captureRect.width() - m_pToolBar->width(),
                         m_captureRect.y() - m_pToolBar->height());

    }
    else
    {
        m_pToolBar->move(m_captureRect.x() + m_captureRect.width() - m_pToolBar->width(),
                         m_captureRect.y() + m_captureRect.height() + nCaptureBorder);
    }
}

void CaptureView::PlaceScrollPreview()
{
    int rightSpace = width() - m_captureRect.x() - m_captureRect.width();
    int leftSpace = m_captureRect.x();
    int topSpace = m_captureRect.y();
    int bottomSpace = height() - m_captureRect.y() - m_captureRect.height();
    int newHMargin = nScrollPreviewHValue + nScrollPreviewMargin * 2;
    int newVMargin = nScroolPreviewVEMaxHeight + nScrollPreviewMargin * 2;

    if(m_nScrollPreviewX == -1 && m_nScrollPreviewY == -1)
    {
        if(rightSpace < newHMargin &&
                leftSpace < newHMargin)
        {
            // TODO: 两边空间不满足，开始判断上下空间
            if(topSpace < newVMargin &&
                    bottomSpace < newVMargin)
            {
                // TODO: 左右和上下空间都不足，要内嵌在截图区域
                m_enOrientation = Embedded_Top;

                m_scrollImageBk = m_scrollImage.scaled(nScrollPreviewHValue,
                                                       nScroolPreviewVEMinHeight,
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
                m_nScrollPreviewY = m_captureRect.y() + nCaptureBorder + nScrollPreviewMargin;
                m_nScrollPreviewX = m_captureRect.x() + m_captureRect.width() - nScrollPreviewMargin - m_scrollImageBk.width();

            }
            else
            {
                m_scrollImageBk = m_scrollImage.scaled(nScrollPreviewHValue,
                                                       nScroolPreviewVEMinHeight,
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
                if(topSpace > newVMargin)
                {
                    // TODO: 上面空间充足
                    m_enOrientation = Top;
                    m_nScrollPreviewY = m_captureRect.y() - nScrollPreviewMargin - m_scrollImageBk.height();
                }
                else
                {
                    // TODO: 下面空间充足
                    m_enOrientation = Bottom;
                    m_nScrollPreviewY = m_captureRect.y() + m_captureRect.height() + nScrollPreviewMargin;
                }
                m_nScrollPreviewX = m_captureRect.x() + (m_captureRect.width() - m_scrollImageBk.width());

            }
        }
        else
        {
            if(m_scrollImage.width() > nScrollPreviewHValue
                    && m_scrollImage.height() > nScrollPreviewHMaxHeight)
            {
                m_scrollImageBk = m_scrollImage.scaled(nScrollPreviewHValue,
                                                       m_scrollImage.height(),
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
            }
            else if(m_scrollImage.width() > nScrollPreviewHValue
                    && m_scrollImage.height() < nScrollPreviewHMaxHeight)
            {
                m_scrollImageBk = m_scrollImage.scaled(nScrollPreviewHValue,
                                                       m_scrollImage.height(),
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
            }
            else if(m_scrollImage.width() < nScrollPreviewHValue
                    && m_scrollImage.height() > nScrollPreviewHMaxHeight)
            {
                m_scrollImageBk = m_scrollImage.scaled(m_scrollImage.width(),
                                                       nScrollPreviewHMaxHeight,
                                                       Qt::KeepAspectRatio,
                                                       Qt::SmoothTransformation);
            }
            else
            {
                m_scrollImageBk = m_scrollImage.copy(0, 0, m_scrollImage.width(), m_scrollImage.height());
            }
            if(rightSpace < newHMargin)
            {
                // TODO: 左边空间充足
                m_enOrientation = Left;
                m_nScrollPreviewX = m_captureRect.x() - m_scrollImageBk.width() - nScrollPreviewMargin;
            }
            else
            {
                // TODO: 右边空间充足
                m_enOrientation = Right;
                m_nScrollPreviewX = m_captureRect.x() + m_captureRect.width() + nScrollPreviewMargin;
            }
            m_nScrollPreviewY = m_captureRect.y();
        }
    }
    else
    {
        if(m_enOrientation == Right || m_enOrientation == Left)
        {
            int h = 0;
            if(m_scrollImageBk.height() < nScrollPreviewHMaxHeight)
            {
                h = m_scrollImageBk.height() + nScrollStep;
                if(h > nScrollPreviewHMaxHeight)
                    h = nScrollPreviewHMaxHeight;
            }
            else
            {
                h = nScrollPreviewHMaxHeight;
            }

            m_scrollImageBk = m_scrollImage.scaled(m_scrollImageBk.width(),
                                                   h,
                                                   Qt::KeepAspectRatio,
                                                   Qt::SmoothTransformation);
            if(m_enOrientation == Left)
            {
                m_nScrollPreviewX = m_captureRect.x() - m_scrollImageBk.width() - nScrollPreviewMargin;
            }
            else
            {
                m_nScrollPreviewX = m_captureRect.x() + m_captureRect.width() + nScrollPreviewMargin;
            }
            m_nScrollPreviewY = m_captureRect.y();
        }
        else if(m_enOrientation == Top || m_enOrientation == Bottom)
        {
            int h = 0;
            if(m_scrollImageBk.height() < nScroolPreviewVEMaxHeight)
            {
                h = m_scrollImageBk.height() + nScrollStep;
                if(h > nScroolPreviewVEMaxHeight)
                    h = nScroolPreviewVEMaxHeight;
            }
            else
            {
                h = nScroolPreviewVEMaxHeight;
            }
            m_scrollImageBk = m_scrollImage.scaled(m_scrollImageBk.width(),
                                                   h,
                                                   Qt::KeepAspectRatio,
                                                   Qt::SmoothTransformation);
            if(m_enOrientation == Top)
            {
                m_nScrollPreviewY = m_captureRect.y() - nScrollPreviewMargin - m_scrollImageBk.height();
            }
            else
            {
                m_nScrollPreviewY = m_captureRect.y() + m_captureRect.height() + nScrollPreviewMargin;
            }
            m_nScrollPreviewX = m_captureRect.x() + (m_captureRect.width() - m_scrollImageBk.width());
        }
        else
        {
            int h = 0;
            if(m_scrollImageBk.height() < nScroolPreviewVEMinHeight)
            {
                h = m_scrollImageBk.height() + nScrollStep;
                if(h > nScroolPreviewVEMaxHeight)
                    h = nScroolPreviewVEMaxHeight;
            }
            else
            {
                h = nScroolPreviewVEMaxHeight;
            }
            m_scrollImageBk = m_scrollImage.scaled(nScrollPreviewHValue,
                                                   h,
                                                   Qt::KeepAspectRatio,
                                                   Qt::SmoothTransformation);
            m_nScrollPreviewX = m_captureRect.x() + m_captureRect.width() - nScrollPreviewMargin - m_scrollImageBk.width();
            if(m_enOrientation == Embedded_Top)
            {
                m_nScrollPreviewY = m_captureRect.y() + nCaptureBorder + nScrollPreviewMargin;
            }
            else // Embedded_Bottom
            {
                m_nScrollPreviewY = m_captureRect.y() + nCaptureBorder + m_captureRect.height() -
                        nScrollPreviewMargin - m_scrollImageBk.height();
            }
        }
    }
}

void CaptureView::CaptureRegionTransform(const QPointF &ptDiff)
{
    if(ptDiff.x() > 0 && ptDiff.y() > 0)
    {
        // TODO: x大于0，y大于0，则往右下角移动
        m_captureRect = QRect(m_ptPressed, m_ptCurrent);
    }
    else if(ptDiff.x() > 0 && ptDiff.y() < 0)
    {
        // TODO: x大于0，y小于0，则往右上角移动
        m_captureRect = QRect(m_ptPressed.x(),
                              m_ptPressed.y() + ptDiff.y(),
                              ptDiff.x(),
                              std::abs(ptDiff.y()));
    }
    else if(ptDiff.x() < 0 && ptDiff.y() > 0)
    {
        // TODO: x小于0，y大于0，则往左下角移动
        m_captureRect = QRect(m_ptPressed.x() + ptDiff.x(),
                              m_ptPressed.y(),
                              std::abs(ptDiff.x()),
                              ptDiff.y());

    }
    else if(ptDiff.x() < 0 && ptDiff.y() < 0)
    {
        // TODO: x小于0，y小于0，则往左上角移动
        m_captureRect = QRect(m_ptPressed.x() + ptDiff.x(),
                              m_ptPressed.y() + ptDiff.y(),
                              std::abs(ptDiff.x()),
                              std::abs(ptDiff.y()));
    }
    else
    {
        // TODO: 暂时不做处理
    }
}

void CaptureView::FinishCallback(bool b, const QPixmap &pixmap)
{
    if(m_bScrollCapture)
    {
        // TODO: 长截图要等待队列全部结束
        m_seQueue.Wait();
    }
    emit CaptureFinished(b, pixmap);
    deleteLater();
}


//////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

ScreenCaptureEngine::ScreenCaptureEngine(QObject *parent) : QObject(parent)
{

}

ScreenCaptureEngine::~ScreenCaptureEngine()
{

}

void ScreenCaptureEngine::Start(bool bHide)
{
    Q_UNUSED(bHide)
    CaptureView* view = new CaptureView(nullptr);
    connect(view, &CaptureView::CaptureFinished, this, [&](bool b, QPixmap pixmap) {
        emit CaptureFinished(b, pixmap);
        deleteLater();
    });
    view->move(0, 0);
    view->show();
    view->raise();
}

//////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////


ToolBar::ToolBar(QWidget *parent) : QWidget(parent)
{
    setObjectName(QStringLiteral("tool_bar"));
    setStyleSheet(QStringLiteral("QWidget#tool_bar{border:none;background-color:transparent;}"));
    setFixedSize(5, 100);
    m_nPos = 0;
}

ToolBar::~ToolBar()
{

}

void ToolBar::Push(QPushButton *pButton)
{
    if(pButton)
    {
        int nHeight = pButton->height();
        if(height() < nHeight)
            nHeight = height();
        int nWidth = width();
        if(nWidth < pButton->width())
            nWidth = pButton->width();
        else
            nWidth += pButton->width();
        setFixedSize(nWidth, nHeight);
        pButton->move(m_nPos, 0);
        m_nPos += pButton->width();
    }
}

void ToolBar::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QWidget::paintEvent(event);
}
